/**
 * @author HuWen
 * @date 2025/8/11 11:03)
 */
import java.sql.*;
import java.util.*;
import java.util.stream.Collectors;

public class LotteryMarkovPredictor {
    // 数据库连接信息（替换为实际配置）
    private static final String DB_URL = "jdbc:mysql://192.168.110.254:3306/test?useSSL=false&serverTimezone=UTC";
    private static final String DB_USER = "root";
    private static final String DB_PASSWORD = "root123456";

    // 超级大乐透规则：前区1-35，后区1-12；前区5个不重复，后区2个不重复
    private static final int FRONT_MIN = 1;
    private static final int FRONT_MAX = 35;
    private static final int BACK_MIN = 1;
    private static final int BACK_MAX = 12;
    private static final int FRONT_COUNT = 5;
    private static final int BACK_COUNT = 2;

    public static void main(String[] args) {
        try {
            // 1. 从数据库读取历史开奖结果
            List<List<Integer>> frontHistory = new ArrayList<>(); // 前区历史数字（每组5个）
            List<List<Integer>> backHistory = new ArrayList<>();  // 后区历史数字（每组2个）
            readHistoryData(frontHistory, backHistory);

            if (frontHistory.isEmpty() || backHistory.isEmpty()) {
                System.out.println("没有足够的历史数据用于预测");
                return;
            }

            // 2. 构建前区和后区的状态转移矩阵（数字 -> 下一个数字的概率）
            Map<Integer, Map<Integer, Double>> frontTransition = buildTransitionMatrix(frontHistory, FRONT_MIN, FRONT_MAX);
            Map<Integer, Map<Integer, Double>> backTransition = buildTransitionMatrix(backHistory, BACK_MIN, BACK_MAX);

            // 3. 获取最近一期的数字作为初始状态
            List<Integer> lastFront = frontHistory.get(frontHistory.size() - 1);
            List<Integer> lastBack = backHistory.get(backHistory.size() - 1);

            // 4. 预测下一期可能的数字组合（生成5组候选）
            System.out.println("基于马尔可夫链的预测结果（仅供参考，彩票为随机事件）：");
            for (int i = 0; i < 5; i++) {
                List<Integer> predictFront = predictNext(frontTransition, lastFront, FRONT_COUNT, FRONT_MIN, FRONT_MAX);
                List<Integer> predictBack = predictNext(backTransition, lastBack, BACK_COUNT, BACK_MIN, BACK_MAX);
                System.out.printf("候选%d：前区=%s 后区=%s%n",
                        i + 1,
                        predictFront.stream().map(String::valueOf).collect(Collectors.joining(" ")),
                        predictBack.stream().map(String::valueOf).collect(Collectors.joining(" ")));
            }

        } catch (SQLException e) {
            e.printStackTrace();
        }
    }

    // 从数据库读取历史开奖数据，拆分前区和后区
    private static void readHistoryData(List<List<Integer>> frontHistory, List<List<Integer>> backHistory) throws SQLException {
        String sql = "SELECT lottery_draw_result FROM lottery_draw ORDER BY lottery_draw_time ASC";

        try (Connection conn = DriverManager.getConnection(DB_URL, DB_USER, DB_PASSWORD);
             PreparedStatement pstmt = conn.prepareStatement(sql);
             ResultSet rs = pstmt.executeQuery()) {

            while (rs.next()) {
                String result = rs.getString("lottery_draw_result");
                if (result == null || result.trim().isEmpty()) continue;

                // 解析开奖结果：前5个为前区，后2个为后区（如"06 14 19 22 27 01 04"）
                String[] nums = result.trim().split(" ");
                if (nums.length != 7) continue; // 过滤格式错误的数据

                List<Integer> front = new ArrayList<>();
                List<Integer> back = new ArrayList<>();
                for (int i = 0; i < 5; i++) front.add(Integer.parseInt(nums[i]));
                for (int i = 5; i < 7; i++) back.add(Integer.parseInt(nums[i]));

                frontHistory.add(front);
                backHistory.add(back);
            }
        }
    }

    // 构建状态转移矩阵：统计每个数字之后出现其他数字的概率
    private static Map<Integer, Map<Integer, Double>> buildTransitionMatrix(
            List<List<Integer>> history, int min, int max) {

        // 统计转移次数：fromNum -> toNum 的出现次数
        Map<Integer, Map<Integer, Integer>> transitionCount = new HashMap<>();
        // 统计每个数字出现的总次数（用于计算概率）
        Map<Integer, Integer> totalCount = new HashMap<>();

        // 初始化矩阵（包含所有可能的数字）
        for (int num = min; num <= max; num++) {
            transitionCount.put(num, new HashMap<>());
            totalCount.put(num, 0);
        }

        // 遍历历史数据，统计转移次数
        for (List<Integer> draw : history) {
            for (int i = 0; i < draw.size() - 1; i++) {
                int from = draw.get(i);
                int to = draw.get(i + 1);

                // 累加转移次数
                transitionCount.get(from).put(to,
                        transitionCount.get(from).getOrDefault(to, 0) + 1);
                // 累加当前数字的总出现次数
                totalCount.put(from, totalCount.get(from) + 1);
            }
        }

        // 将次数转换为概率（频率估计概率）
        Map<Integer, Map<Integer, Double>> transitionProb = new HashMap<>();
        for (int from = min; from <= max; from++) {
            int total = totalCount.get(from);
            Map<Integer, Double> probMap = new HashMap<>();

            if (total == 0) {
                // 若该数字从未出现过，赋予所有可能数字均等概率
                double equalProb = 1.0 / (max - min + 1);
                for (int to = min; to <= max; to++) {
                    probMap.put(to, equalProb);
                }
            } else {
                // 计算每个转移的概率（次数/总次数）
                for (int to = min; to <= max; to++) {
                    int count = transitionCount.get(from).getOrDefault(to, 0);
                    probMap.put(to, (double) count / total);
                }
            }
            transitionProb.put(from, probMap);
        }

        return transitionProb;
    }

    // 基于转移矩阵预测下一组数字
    private static List<Integer> predictNext(
            Map<Integer, Map<Integer, Double>> transition,
            List<Integer> lastDraw, int needCount, int min, int max) {

        List<Integer> prediction = new ArrayList<>();
        Random random = new Random();

        // 以最近一期的最后一个数字作为初始状态
        int current = lastDraw.get(lastDraw.size() - 1);

        while (prediction.size() < needCount) {
            // 根据转移概率随机选择下一个数字
            Map<Integer, Double> probMap = transition.get(current);
            double rand = random.nextDouble();
            double cumulative = 0.0;
            int next = -1;

            // 按概率分布选择下一个数字
            for (Map.Entry<Integer, Double> entry : probMap.entrySet()) {
                cumulative += entry.getValue();
                if (cumulative >= rand) {
                    next = entry.getKey();
                    break;
                }
            }

            // 确保数字不重复且在有效范围
            if (next != -1 && !prediction.contains(next) && next >= min && next <= max) {
                prediction.add(next);
                current = next; // 更新当前状态
            }
        }

        // 排序（彩票数字通常按升序展示）
        Collections.sort(prediction);
        return prediction;
    }
}


